
// This file contains functions for modifying GF trees.

// The following global variables are necessary:
// GRAMMAR: a GFGrammar object
// CONTEXTS: a dictionary mapping 'contexts' to 'pruned trees'


/** similar_trees(tree, focus):
    Calculates similar trees from the global CONTEXTS dictionary.
    The similar trees are returned in an array. Since the focus node
    might have moved to another place in the tree, the new path is also
    returned.

    @param tree: a GFTree object
    @param focus: the path to the node that is in focus
    @return: an Array of {'tree': GFTree, 'focus': path to new focus} 
**/
function similar_trees(tree, focus) {
    var simtrees = [];
    for (var p = focus.length; p >= 0; p--) {
        var subpath = focus.slice(0, p);
        var subfocus = focus.slice(p);
        var subtree = getSubtree(tree, subpath);
        _similar_subtrees(subtree, subfocus)
            .forEach(function(simsub){
                var sim = subpath ? updateCopy(tree, subpath, simsub.tree) : simsub.tree;
                var simfocus = subpath.concat(simsub.focus);
                simtrees.push({'tree': sim, 'focus': simfocus});
            });
    }
    return simtrees;
}

function _similar_subtrees(subtree, focuspath) {
    var similars = [];
    var simset = {};
    simset[strTree(subtree, focuspath)] = true;
    _prune_tree(subtree).forEach(function(nodes){
        var focusindex = nodes.indexOf(focuspath);
        if (focuspath && focusindex < 0)
            return;
        var pruned_trees = nodes.map(function(node){return getSubtree(subtree, node)});
        var context = pruned_trees.map(function(sub){return sub.type});
        context.unshift(subtree.type);
        var metas = CONTEXTS[context.join(":")];
        if (metas && metas.length > 1) {
            metas.forEach(function(meta){
                var simtree = _instantiate_metatree(meta, pruned_trees, focusindex);
                var simkey = strTree(simtree.tree, simtree.focus);
                if (!simset[simkey]) {
                    simset[simkey] = true;
                    similars.push(simtree);
                }
            });
        }
    });
    return similars;
}

function _instantiate_metatree(metatree, subtrees, focusindex) {
    var focuspath = "";
    var index = 0;
    function _build(subtree, path) {
        if (subtree.node) {
            var children = subtree.children.map(function(child, i){
                return _build(child, path.concat(i));
            });
            return GFTree(subtree.node, subtree.type, children);
        } else {
            if (index == focusindex)
                focuspath = path;
            return subtrees[index++];
        }
    }
    var tree = _build(metatree, "");
    return {'tree': tree, 'focus': focuspath};
}

function _prune_tree(tree, path) {
    if (path == null) path = "";
    var metatrees = productlist(tree.children.map(function(child, i){
        return _prune_tree(child, path.concat(i));
    }));
    metatrees.unshift([path]);
    return metatrees;
}


//////////////////////////////////////////////////////////////////////

function _test_simtrees(lang, ex, focus) {
    if (!lang) lang = "TinyEng";
    if (!ex) ex = "truckdrivers";
    if (!focus) focus = "";
    var tree = examples[ex];
    console.log(strTree(tree, focus));
    var lin = GRAMMAR.linearise(lang, tree);
    console.log(strLin(lin, focus));
    var foclen = focus.length;
    similar_trees(tree, focus)
        .forEach(function(sim,j){
            console.log(j+".", strTree(sim.tree, sim.focus));
            var simlin = GRAMMAR.linearise(lang, sim.tree);
            console.log("  ", strLin(simlin, sim.focus));
        });
}
